package org.seedltd.upms.service.impl.detection;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.seedltd.base.exception.BusinessException;
import org.seedltd.core.base.BasePage;
import org.seedltd.core.base.BaseServiceImpl;
import org.seedltd.core.utils.*;
import org.seedltd.oss.service.IOssService;
import org.seedltd.springsecurity.utils.ResponseUtils;
import org.seedltd.upms.constant.DictKeyConst;
import org.seedltd.upms.constant.FolderBucket;
import org.seedltd.upms.constant.GlobalConst;
import org.seedltd.upms.constant.ParamKeyConst;
import org.seedltd.upms.mapper.detection.DetectionQrcodeMapper;
import org.seedltd.upms.model.detection.entity.DetectionQrcode;
import org.seedltd.upms.model.detection.form.DetectionQrcodeSearchForm;
import org.seedltd.upms.model.detection.form.DetectionTubeForm;
import org.seedltd.upms.model.detection.vo.DetectionQrcodeVO;
import org.seedltd.upms.model.sys.entity.SysParam;
import org.seedltd.upms.model.sys.vo.DictEnumVO;
import org.seedltd.upms.service.detection.DetectionQrcodeService;
import org.seedltd.upms.service.detection.DetectionTubeService;
import org.seedltd.upms.service.sys.SysDictService;
import org.seedltd.upms.service.sys.SysParamService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import com.google.common.collect.Lists;
import com.itextpdf.text.DocumentException;

/**
 * 二维码数据业务实现
 * 
 * @author laizuan
 * @since 2021/07/26
 */
@Service
public class DetectionQrcodeServiceImpl extends BaseServiceImpl<DetectionQrcodeMapper, DetectionQrcode>
    implements DetectionQrcodeService {
    private final IOssService ossService;
    private final SysParamService sysParamService;
    private final SysDictService sysDictService;
    private final TemplateEngine templateEngine;
    private final DetectionTubeService detectionTubeService;

    @Value("${seeldtd.logoPath:static/logo.png}")
    private String logoPath;

    public DetectionQrcodeServiceImpl(IOssService ossService, SysParamService sysParamService,
        SysDictService sysDictService, TemplateEngine templateEngine, DetectionTubeService detectionTubeService) {
        this.ossService = ossService;
        this.sysParamService = sysParamService;
        this.sysDictService = sysDictService;
        this.templateEngine = templateEngine;
        this.detectionTubeService = detectionTubeService;
    }

    @Override
    public DetectionQrcode findByCodeNo(String codeNo, boolean check) {
        DetectionQrcode detectionQrcode = q().eq(DetectionQrcode::getCodeNumber, codeNo).selectOne();
        if (check) {
            if (detectionQrcode == null) {
                throw new BusinessException("未找到二维码数据【%s】", codeNo);
            }
            Integer state = detectionQrcode.getState();
            if (GlobalConst.QRCODE_STATE_UNBINDING.equals(state)) {
                return detectionQrcode;
            }
            if (GlobalConst.QRCODE_STATE_BINDING.equals(state)) {
                throw new BusinessException("二维码【%s】已绑定", codeNo);
            }
            if (GlobalConst.QRCODE_STATE_INVALID.equals(state)) {
                throw new BusinessException("二维码【%s】已失效", codeNo);
            }
        }
        return detectionQrcode;
    }

    @Override
    public DetectionQrcodeVO findById(Long id) {
        DetectionQrcode pojo = baseMapper.selectById(id);
        if (pojo == null) {
            return null;
        }
        DetectionQrcodeVO resultVo = new DetectionQrcodeVO();
        BeanUtils.copyProperties(pojo, resultVo);
        return resultVo;
    }

    @Override
    @Transactional
    public void add(Integer count) throws Exception {
        SysParam qrCodeConfig = sysParamService.getByKey(ParamKeyConst.QR_CODE_CONFIG);
        String widthStr = Optional.ofNullable(qrCodeConfig).map(SysParam::getParamValue).orElse("120");
        int size = Integer.parseInt(widthStr);
        SysParam qrContent = sysParamService.getByKey(ParamKeyConst.QR_CODE_CONTENT);
        String content = Optional.ofNullable(qrContent).map(SysParam::getParamValue).orElse("");

        DetectionQrcode pojo;
        DetectionTubeForm detectionTubeForm;
        for (int i = 0; i < count; i++) {
            final String number = getNumber();
            HashMap<String, String> params = new HashMap<>(2);
            params.put("codeNo", number);
            StringSubstitutor sub = new StringSubstitutor(params);
            pojo = new DetectionQrcode();
            byte[] bytes = QrCodeUtils.encodeToByte(sub.replace(content), null, false, size, 10);
            String path = ossService.upload(FolderBucket.QRCODE, number.concat(".jpg"), bytes);
            pojo.setPath(path);
            pojo.setCodeNumber(number);
            pojo.setState(GlobalConst.QRCODE_STATE_UNBINDING);
            this.save(pojo);

            detectionTubeForm = new DetectionTubeForm();
            detectionTubeForm.setCodeNumber(number);
            detectionTubeService.add(detectionTubeForm);
        }

    }

    private String getNumber() {
        String prefix = DateUtils.formatDate(new Date(), DateUtils.YYYYMMDD);
        String numeric = RandomStringUtils.randomNumeric(5);
        String code = prefix.concat(numeric);
        if (baseMapper.checkCodeNumberExists(code) != null) {
            getNumber();
        }
        return code;
    }

    @Override
    public boolean disabled(Long id) {
        DetectionQrcode qrcode = baseMapper.selectById(id);
        if (qrcode == null) {
            throw new BusinessException("未找到需要修改的二维码数据");
        }

        if (GlobalConst.QRCODE_STATE_INVALID.equals(qrcode.getState())) {
            throw new BusinessException("已失效的二维码不能处理。");
        }

        DetectionQrcode pojo = new DetectionQrcode();
        pojo.setId(id);
        pojo.setState(GlobalConst.QRCODE_STATE_INVALID);
        return this.updateById(pojo);
    }

    @Override
    public void print(HttpServletResponse response, String Ids, String type) throws IOException, DocumentException {
        if (StringUtils.isBlank(Ids) || StringUtils.isBlank(type)) {
            ResponseUtils.write(response, "500", "缺少参数");
            return;
        }
        DictEnumVO dict = sysDictService.findByTypeAndValue(DictKeyConst.PRINT_NOTES, type);
        if (dict == null) {
            ResponseUtils.write(response, "500", "请先在字典表配置打印内容");
            return;
        }
        String[] codeIdArray = StringUtils.split(Ids, ",");
        Set<String> codeIdList = new HashSet<>(Arrays.asList(codeIdArray));

        Map<String, Object> params = new HashMap<>();
        List<String> contents = Lists.newArrayList();
        File logoFile = ResourceUtils.getFile(logoPath);
        String logoImg = Base64.encodeBase64String(FileUtils.readFileToByteArray(logoFile));

        for (String codeId : codeIdList) {
            if (StringUtils.isBlank(codeId)) {
                continue;
            }
            DetectionQrcode detectionQrCode = this.baseMapper.selectById(Long.valueOf(codeId));
            if (detectionQrCode == null || GlobalConst.QRCODE_STATE_INVALID.equals(detectionQrCode.getState())
                || StringUtils.isBlank(detectionQrCode.getPath())) {
                continue;
            }

            params.put("logoImg", logoImg);
            params.put("text", dict.getDesc());
            params.put("codeNo", detectionQrCode.getCodeNumber());
            params.put("length", "50mm");
            params.put("width", "20mm");
            byte[] bytes = ossService.download(FolderBucket.QRCODE, detectionQrCode.getPath());
            String qrImg = Base64.encodeBase64String(bytes);
            params.put("qrImg", qrImg);
            contents.add(this.render(params, templateEngine));
        }
        setResponse(response, "tag.pdf", contents);
    }

    private String render(Map<String, Object> dataMap, TemplateEngine templateEngine) {
        Context context = new Context();
        context.setVariables(dataMap);
        return templateEngine.process(GlobalConst.REPORT_TAG_PATH, context);
    }

    private void setResponse(HttpServletResponse response, String fileName, List<String> contents)
        throws IOException, DocumentException {
        fileName = URLEncoder.encode(fileName.concat(".pdf"), StandardCharsets.UTF_8);
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "inline;fileName=".concat(fileName));
        response.setContentType("application/pdf");
        List<byte[]> pdfList = Lists.newArrayList();
        for (String content : contents) {
            pdfList.add(PdfUtils.createPDF(content, "fonts/simsun.ttc"));
        }
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            PdfUtils.mergePdfFiles(pdfList, outputStream);
        }
    }

    @Override
    public boolean delete(Long id) {
        DetectionQrcode qrcode = baseMapper.selectById(id);
        if (qrcode == null) {
            return true;
        }

        if (GlobalConst.QRCODE_STATE_BINDING.equals(qrcode.getState())) {
            throw new BusinessException("已绑定的二维码不能删除。");
        }

        String path = qrcode.getPath();
        ossService.delete(FolderBucket.QRCODE, path);
        return baseMapper.deleteById(id) > 0;
    }

    @Override
    public BasePage<DetectionQrcodeVO> page(DetectionQrcodeSearchForm searchForm) {
        return q(searchForm).selectPage(searchForm.getStart(), searchForm.getSize(), DetectionQrcodeVO.class);
    }
}